#include "Renderer.h"

#define min(a, b) (a < b ? a : b)
#define max(a, b) (a > b ? a : b)

Renderer::Renderer(int w, int h)
{
	m_width = w;
	m_height = h;
	m_img = TGAImage(w, h, TGAImage::RGBA);
}

void Renderer::DrawLine(int x0, int y0, int x1, int y1, TGAColor color)
{
	int lenx = abs(x0 - x1) + 1;
	int leny = abs(y0 - y1) + 1;
	int len = lenx > leny ? lenx : leny;
	for (float i = 0; i < len; i++)
	{
		float t = i / (len - 1);
		int x = x0 + t * (x1 - x0) + 0.5;
		int y = y0 + t * (y1 - y0) + 0.5;
		m_img.set(x, y, color);
	}
}

void Renderer::DrawLine(int x0, int y0, float z0, int x1, int y1, float z1, float* zbuffer, TGAColor color)
{
	int lenx = abs(x0 - x1) + 1;
	int leny = abs(y0 - y1) + 1;
	int len = lenx > leny ? lenx : leny;
	for (float i = 0; i < len; i++)
	{
		float t = i / (len - 1);
		int x = x0 + t * (x1 - x0) + 0.5;
		int y = y0 + t * (y1 - y0) + 0.5;
		float z = z0 + t * (z1 - z0);
		int id = y * m_width + x;
		if (z > zbuffer[id])
		{
			zbuffer[id] = z;
			m_img.set(x, y, color);
		}
	}
}

void Renderer::DrawLineWithUV(
	float x0, float y0, float z0, Vec2f uv_start,
	float x1, float y1, float z1, Vec2f uv_end,
	float* zbuffer, TGAImage* imgae)
{
	int lenx = abs(x0 - x1) + 1.5;
	int leny = abs(y0 - y1) + 1.5;
	int len = lenx > leny ? lenx : leny;
	for (float i = 0; i < len; i++)
	{
		float t = i / (len - 1);
		int x = x0 + t * (x1 - x0) + 0.5;
		int y = y0 + t * (y1 - y0) + 0.5;
		float z = z0 + t * (z1 - z0);
		float t2 = (x - x0) / (x1 - x0);
		float t3 = (y - y0) / (y1 - y0);
		int u = (uv_start.x + (uv_end.x - uv_start.x) * t) * imgae->get_width();
		int v = (uv_start.y + (uv_end.y - uv_start.y) * t) * imgae->get_height();
		int id = y * m_width + x;
		auto color = imgae->get(u, v);
		color = color.Gamma(1.5).Mulit(m_intensity);
		if (z > zbuffer[id])
		{
			zbuffer[id] = z;
			m_img.set(x, y, color);
		}
	}
}


void Renderer::DrawMeshLine(Model* model, TGAColor color)
{
	for (int i = 0; i < model->nfaces(); i++) {
		std::vector<int> face = model->face(i);

		for (int j = 0; j < 3; j++) {
			Vec3f v0 = model->vert(face[j]);
			Vec3f v1 = model->vert(face[(j + 1) % 3]);
			int x0 = (v0.x + 1.)*m_width / 2.;
			int y0 = (v0.y + 1.)*m_height / 2.;
			int x1 = (v1.x + 1.)*m_width / 2.;
			int y1 = (v1.y + 1.)*m_height / 2.;
			DrawLine(x0, y0, x1, y1, color);
			
		}
	}
}


void Renderer::DrawMeshFace(Model* model, TGAColor color, Vec3f light_dir, float* zbuffer)
{
	for (int i = 0; i < model->nfaces(); i++) {
		std::vector<int> face = model->face(i);
		Vec3f screen_coords[3];
		Vec3f world_coords[3];
		for (int j = 0; j < 3; j++) {
			Vec3f v = model->vert(face[3 * j + 0]);
			float x = (v.x + 1.)* m_width / 2.;
			float y = (v.y + 1.)* m_height / 2.;
			float z = v.z + 1.0;
			screen_coords[j] = Vec3f(x, y, z);
			world_coords[j] = v;
		}
		Vec3f n = (world_coords[2] - world_coords[0]) ^ (world_coords[1] - world_coords[0]);
		n.normalize();
		m_intensity = n * light_dir.normalize();
		m_intensity = m_intensity * .5 + .5;
		if (m_intensity >= 0)
		{
			TGAColor color(255 * m_intensity, 255 * m_intensity, 255 * m_intensity, 255);
			//TGAColor color(rand() % 256, rand() % 256, rand() % 256, 255);
			DrawTriangle(screen_coords, zbuffer, color);
		}
	}

}

void Renderer::DrawMeshFaceWithUV(Model* model, TGAImage* imgae, Vec3f light_dir, float* zbuffer)
{
	for (int i = 0; i < model->nfaces(); i++) {
		std::vector<int> face = model->face(i);
		Vec3f screen_coords[3];
		Vec3f world_coords[3];
		Vec2f uvs[3];
		for (int j = 0; j < 3; j++) {
			Vec3f v = model->vert(face[3 * j + 0]);
			float x = (v.x + 1.)* m_width / 2.;
			float y = (v.y + 1.)* m_height / 2.;
			float z = v.z + 1.0;
			screen_coords[j] = Vec3f(x, y, z);
			world_coords[j] = v;
			uvs[j] = model->uv(face[3 * j + 1]);
		}
		Vec3f n = (world_coords[2] - world_coords[0]) ^ (world_coords[1] - world_coords[0]);
		n.normalize();
		m_intensity = n * light_dir.normalize();
		
		m_intensity = m_intensity * .5 + .5;
		DrawTriangleWithUV(screen_coords, uvs, zbuffer, imgae);
		
	}
	

}


void Renderer::DrawTriangleWithUV(Vec3f *pts, Vec2f *uvs, float *zbuffer, TGAImage* image)
{
	int left = m_width - 1;
	int right = 0;
	int bottom = m_height - 1;
	int up = 0;
	for (size_t i = 0; i < 3; i++)
	{
		Vec3f p = pts[i];
		left = min(p.x, left);
		right = max(p.x, right);
		bottom = min(p.y, bottom);
		up = max(p.y, up);
	}
	left = max(0, left);
	right = min(m_width - 1, right);

	bottom = max(0, bottom);
	up = min(m_height - 1, up);

	int len = min(right - left, up - bottom) + 1;
	bool isReverse = right - left < up - bottom;
	for (size_t i = 0; i < len; i++)
	{
		float start = max(m_width, m_height) - 1;
		float end = 0;
		float z_start = 0;
		float z_end = 0;
		Vec2f uv_start(0, 0);
		Vec2f uv_end(0, 0);
		float current = isReverse ? left + i : bottom + i;


		for (size_t j = 0; j < 3; j++)
		{
			Vec3f p0 = pts[j];
			Vec3f p1 = pts[(j + 1) % 3];
			Vec2f uv0 = uvs[j];
			Vec2f uv1 = uvs[(j + 1) % 3];
			float t = -1;
			if (isReverse && p1.x != p0.x) t = (current - p0.x) / (p1.x - p0.x);
			else if (!isReverse && p1.y != p0.y) t = (current - p0.y) / (p1.y - p0.y);
			if (t < 0 || t > 1) continue;
			if (isReverse)
			{
				int y = p0.y + (p1.y - p0.y) * t;
				if (y < start)
				{
					start = y;
					z_start = p0.z + (p1.z - p0.z) * t;
					uv_start = uv0 + (uv1 - uv0) * t;
				}
				if (y > end)
				{
					end = y;
					z_end = p0.z + (p1.z - p0.z) * t;
					uv_end = uv0 + (uv1 - uv0) * t;
				}
			}
			else
			{
				int x = p0.x + (p1.x - p0.x) * t;

				if (x < start)
				{
					start = x;
					z_start = p0.z + (p1.z - p0.z) * t;
					uv_start = uv0 + (uv1 - uv0) * t;
				}
				if (x > end)
				{
					end = x;
					z_end = p0.z + (p1.z - p0.z) * t;
					uv_end = uv0 + (uv1 - uv0) * t;
				}
			}
		}
		if (start > end) continue;
		if (isReverse) DrawLineWithUV(
			current, start, z_start, uv_start,
			current, end, z_end, uv_end,
			zbuffer, image);
		else DrawLineWithUV(
			start, current, z_start, uv_start,
			end, current, z_end, uv_end,
			zbuffer, image);

	}
}

void Renderer::DrawTriangle(Vec3f *pts, float *zbuffer, TGAColor color)
{
	int left = m_width - 1;
	int right = 0;
	int bottom = m_height - 1;
	int up = 0;
	for (size_t i = 0; i < 3; i++)
	{
		Vec3f p = pts[i];
		left = min(p.x, left);
		right = max(p.x, right);
		bottom = min(p.y, bottom);
		up = max(p.y, up);
	}
	left = max(0, left);
	right = min(m_width - 1, right);

	bottom = max(0, bottom);
	up = min(m_height - 1, up);

	int len = min(right - left, up - bottom) + 1;
	bool isReverse = right - left < up - bottom;
	for (size_t i = 0; i < len; i++)
	{
		int start = max(m_width, m_height) - 1;
		int end = 0;
		float z_start = 0;
		float z_end = 0;
		float current = isReverse ? left + i : bottom + i;
		
		
		for (size_t j = 0; j < 3; j++)
		{
			Vec3f p0 = pts[j];
			Vec3f p1 = pts[(j + 1) % 3];
			float t = -1;
			if (isReverse && p1.x != p0.x) t = (current - p0.x) / (p1.x - p0.x);
			else if (!isReverse && p1.y != p0.y) t = (current - p0.y) / (p1.y - p0.y);
			if (t < 0 || t > 1) continue;
			if (isReverse)
			{
			 	int y = p0.y + (p1.y - p0.y) * t;
				if (y < start)
				{
					start = y;
					float tz = (float(y) - p0.y) / (p1.y - p0.y);
					z_start = p0.z + (p1.z - p0.z) * tz;
				}
				if (y > end)
				{
					end = y;
					float tz = (float(y) - p0.y) / (p1.y - p0.y);
					z_end = p0.z + (p1.z - p0.z) * tz;
				}
			}
			else
			{
				int x = p0.x + (p1.x - p0.x) * t;

				if (x < start)
				{
					start = x;
					float tz = (float(x) - p0.x) / (p1.x - p0.x);
					z_start = p0.z + (p1.z - p0.z) * tz;
				}
				if (x > end)
				{
					end = x;
					float tz = (float(x) - p0.x) / (p1.x - p0.x);
					z_end = p0.z + (p1.z - p0.z) * tz;
				}
			}
		}
		if (start > end) continue;
		if (isReverse) DrawLine(current, start, z_start, current, end, z_end, zbuffer, color);
		else DrawLine(start, current, z_start, end, current, z_end, zbuffer, color);

	}
}

void Renderer::OutPut()
{
	m_img.flip_vertically();
	m_img.write_tga_file("output.tga");
}

bool Renderer::IsInTriangle(Vec2i * pts, Vec2i p)
{
	return false;
}


